/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to you under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.calcite.adapter.hive;

import org.apache.calcite.adapter.enumerable.EnumerableRel;
import org.apache.calcite.config.CalciteConnectionConfigImpl;
import org.apache.calcite.config.CalciteConnectionProperty;
import org.apache.calcite.config.Lex;
import org.apache.calcite.rex.RexExecutor;
import org.apache.calcite.runtime.Bindable;
import org.apache.calcite.schema.Schemas;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.parser.SqlParser;
import org.apache.calcite.sql.util.ChainedSqlOperatorTable;
import org.apache.calcite.sql.validate.SqlConformanceEnum;
import org.apache.calcite.table.TableConfig;
import org.apache.calcite.table.TableEnv;

import org.apache.hadoop.hive.conf.HiveConf;
import org.apache.hadoop.hive.ql.session.SessionState;

import com.google.common.collect.Maps;

import org.reflections.Reflections;

import java.util.Properties;

/**
 * HiveTableEnv do:
 * 1. use a {@link HiveSqlOperatorTable} and a {@link HiveSqlParserImpl}
 * to support hive sql query,HiveSqlParserImpl is generated by a customized
 * Parser.jj.
 * 2. setup a new hive session per sql query
 */
public class HiveTableEnv extends TableEnv {

  private static final HiveConf HIVE_CONF = new HiveConf();

  public HiveTableEnv(TableConfig tableConfig) {
    super(tableConfig);
  }


  /**
   * @param hiveFunctionPackages user-defined hiveFunctionPackages to scan
   *                             and register,
   *                             {@link HiveSqlOperatorTable#registerUDF(Reflections)}
   */
  public static synchronized void registerHiveFunctionPackages(
      String... hiveFunctionPackages) {
    if (hiveFunctionPackages.length > 0) {
      HiveSqlOperatorTable.registerHiveFunctionPackages(hiveFunctionPackages);
    }
  }

  public static TableEnv getTableEnv() {
    TableConfig tableConfig = new TableConfig();
    tableConfig.setSqlOperatorTable(
        ChainedSqlOperatorTable.of(HiveSqlOperatorTable.instance(),
            SqlStdOperatorTable.instance()));
    tableConfig.setSqlParserConfig(SqlParser
        .configBuilder()
        .setLex(Lex.JAVA).setCaseSensitive(false).setConformance(
            SqlConformanceEnum.HIVE)
        .setParserFactory(HiveSqlParserImpl.FACTORY)
        .build());
//    tableConfig.setRelDataTypeSystem(new HiveTypeSystemImpl());
    Properties prop = new Properties();
    prop.setProperty(CalciteConnectionProperty.CASE_SENSITIVE.camelName(),
        String.valueOf(tableConfig.getSqlParserConfig().caseSensitive()));
    tableConfig.setCalciteConnectionConfig(
        new CalciteConnectionConfigImpl(prop));
    tableConfig.setConvertletTable(new HiveConvertletTable());
    RexExecutor rexExecutor = new HiveRexExecutorImpl(
        Schemas.createDataContext(null, null));
    tableConfig.setRexExecutor(rexExecutor);
    TableEnv tableEnv = new HiveTableEnv(tableConfig);
    //add table functions
    tableEnv.addFunction("", "explode",
        "org.apache.calcite.adapter.hive.udtf.UDTFExplode", "eval");
    return tableEnv;
  }

  @Override protected void executeBeforeSqlQuery(String sql) {
    //clear thread context
    HiveUDFInstanceCollecterPerSqlQuery.clear();
    //prepare hive session state per sql query
    if (SessionState.get() == null) {
      SessionState ss = new SessionState(HIVE_CONF);
      SessionState.setCurrentSessionState(ss);
    }
    SessionState.get().setupQueryCurrentTimestamp();
  }

  @Override protected Bindable toBindable(EnumerableRel rel) {
    return HiveEnumerableInterpretable.toBindable(Maps.newHashMap(),
        null, rel, EnumerableRel.Prefer.ARRAY);
  }
}

// End HiveTableEnv.java
